A navigation bar (navbar) is a UI element typically placed at the top or side of a web app.
Use a <nav>
with flex
, justify-between
, items-center
.
Add buttons or links like Home, Search, About. (the links can be empty now)
use this as a reference: link
link to project:
π Suggested Folder for navbar component: src/shared/components/Navbar
π Suggested Folder for images: public/images/
Originally, your transportation search logic and UI may have all been inside one component β which quickly becomes messy and hard to manage as your app grows.
Now weβve split the logic into two proper pages:
SearchPage.jsx
TransportationSearchForm
component.SearchResultsPage.jsx
agent
.This separation improves:
π Suggested Folder for navbar component
Inside your features/transportation
folder:
pages/
βββ SearchPage.jsx
βββ SearchResultsPage.jsx
components/
βββ TransportationSearchForm.jsx
βββ TransportationCard.jsx
SearchPage
Use:
import TransportationSearchForm from "@/features/transportation/transportationSearchForm";
const SearchPage = () => {
return (
<div className="container mx-auto py-6">
<TransportationSearchForm />
</div>
);
};
export default SearchPage;
Keep the form clean and layout minimal.
SearchResultsPage
useParams()
for URL parameters (vehicleId
, fromCityId
, toCityId
)useLocation()
and URLSearchParams
to read query strings (departing
, arriving
)agent
Code Example:
import { useEffect, useState } from "react";
import { useLocation, useParams } from "react-router-dom";
import agent from "@/shared/api/agent";
import { TransportationSearchResult } from "@/shared/models/transportation/transportationSearchResult";
import TransportationCard from "@/features/transportation/transportationCard";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
const SearchResultsPage = () => {
const { vehicleId, fromCityId, toCityId } = useParams();
const vehicleTypeId = vehicleId ? parseInt(vehicleId, 1) : 1;
const fromId = fromCityId ? parseInt(fromCityId, 1) : undefined;
const toId = toCityId ? parseInt(toCityId, 1) : undefined;
const query = useQuery();
const departing = query.get("departing");
const arriving = query.get("arriving");
const [results, setResults] = useState<TransportationSearchResult[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const form = {
vehicleTypeId,
fromCityId: fromId,
toCityId: toId,
startDate: departing || null,
endDate: arriving || null,
};
agent.TransportationSearch.search(form)
.then(setResults)
.catch((err) => console.error(err))
.finally(() => setLoading(false));
}, [vehicleId, fromCityId, toCityId, departing, arriving]);
return (
<div className="container mx-auto py-6">
<h2 className="text-2xl font-bold mb-4">Search Results</h2>
{loading ? (
<p>Loading...</p>
) : results.length === 0 ? (
<p>No results found.</p>
) : (
<div className="space-y-4">
{results.map((r) => (
<TransportationCard key={r.id} transportation={r} />
))}
</div>
)}
</div>
);
};
export default SearchResultsPage;
This page is responsible for:
When you define this route in App.jsx
:
<Route path="/:vehicleId/:fromCityId/:toCityId" element={<SearchResultsPage />} />
It means the URL will look like:
/1/21/45
Those values are extracted using:
const { vehicleId, fromCityId, toCityId } = useParams();
πΉ useParams()
comes from React Router and gives you access to the dynamic parts of the URL.
Suppose your full URL is:
/1/21/45?departing=2025-06-01&arriving=2025-06-10
These extra values after the ?
are query string parameters. They're accessed using:
const query = useQuery(); // Custom helper
const departing = query.get("departing");
const arriving = query.get("arriving");
The helper useQuery()
is:
function useQuery() {
return new URLSearchParams(useLocation().search);
}
This uses React Router's useLocation()
to access the full URL, and then parses the query string.
React Router gives you everything as strings. So:
const vehicleTypeId = vehicleId ? parseInt(vehicleId, 10) : 1;
const fromId = fromCityId ? parseInt(fromCityId, 10) : undefined;
const toId = toCityId ? parseInt(toCityId, 10) : undefined;
This ensures you have numbers, not strings, when building your form object.
Now all data is combined into one form
object:
const form = {
vehicleTypeId,
fromCityId: fromId,
toCityId: toId,
startDate: departing || null,
endDate: arriving || null,
};
Then it sends that to the backend:
agent.TransportationSearch.search(form)
.then(setResults)
.catch(err => console.error(err))
.finally(() => setLoading(false));
TransportationSearchForm
:use this as a reference: link
handleSearch
to Navigate with Parametersconst handleSearch = () => {
if (!form.fromCityId || !form.toCityId || !form.startDate || !form.vehicleTypeId)
return;
const params = new URLSearchParams();
if (form.startDate instanceof Date)
params.append("departing", form.startDate.toISOString());
else if (typeof form.startDate === "string")
params.append("departing", form.startDate);
if (form.endDate instanceof Date)
params.append("arriving", form.endDate.toISOString());
else if (typeof form.endDate === "string")
params.append("arriving", form.endDate);
navigate(
`/${form.vehicleTypeId}/${form.fromCityId}/${form.toCityId}?${params.toString()}`
);
};
Changes made:
URLSearchParams
for departing
and arriving
dates.navigate(...)
to go to a route like:
/1/2/3?departing=2025-06-15T00%3A00%3A00.000Z&arriving=2025-06-18T00%3A00%3A00.000Z
That route (
/vehicleTypeId/fromCityId/toCityId
) will be handled by yourSearchResultPage
viareact-router
.
useNavigate
from react-router-dom
import { useNavigate } from "react-router-dom";
App.tsx
as the following:import Navbar from "@/shared/components/navbar";
import "./App.css";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import SearchPage from "@/features/transportation/pages/SearchPage";
import SearchResultsPage from "@/features/transportation/pages/SearchResultsPage";
function App() {
return (
<Router>
<Navbar /> {/* Navbar will show on all pages */}
<div className="pt-16">
{" "}
{/* padding top if navbar is fixed */}
<Routes>
<Route path="/" element={<SearchPage />} />
<Route
path="/:vehicleId/:fromCityId/:toCityId"
element={<SearchResultsPage />}
/>
</Routes>
</div>
</Router>
);
}
export default App;
App.jsx
Router
& Routes
:<Router>
so that React Router can manage navigation. <Routes>
contains all the individual page routes./
: Loads SearchPage
. This is your home/search form. /:vehicleId/:fromCityId/:toCityId
: Loads SearchResultsPage
. This URL carries parameters to display results based on user input.<Routes>
, so it shows on all pages. <div className="pt-16">
adds space at the top so that page content isnβt hidden behind the navbar (assuming the navbar is fixed). npm install react-router-dom
<Router>
<Navbar />
<Routes>
{/* your routes here */}
</Routes>
</Router>
<Route path="/" element={<SearchPage />} />
<Route path="/:vehicleId/:fromCityId/:toCityId" element={<SearchResultsPage />} />